{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "<a href=\"https://githubtocolab.com/gee-community/geemap/blob/master/docs/notebooks/32_supervised_classification.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open in Colab\"/></a>\n",
    "\n",
    "Uncomment the following line to install [geemap](https://geemap.org) if needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "# Machine Learning with Earth Engine - Supervised Classification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "## Supervised classification algorithms available in Earth Engine\n",
    "\n",
    "Source: https://developers.google.com/earth-engine/classification\n",
    "\n",
    "The `Classifier` package handles supervised classification by traditional ML algorithms running in Earth Engine. These classifiers include CART, RandomForest, NaiveBayes and SVM. The general workflow for classification is:\n",
    "\n",
    "1. Collect training data. Assemble features which have a property that stores the known class label and properties storing numeric values for the predictors.\n",
    "2. Instantiate a classifier. Set its parameters if necessary.\n",
    "3. Train the classifier using the training data.\n",
    "4. Classify an image or feature collection.\n",
    "5. Estimate classification error with independent validation data.\n",
    "\n",
    "The training data is a `FeatureCollection` with a property storing the class label and properties storing predictor variables. Class labels should be consecutive, integers starting from 0. If necessary, use remap() to convert class values to consecutive integers. The predictors should be numeric."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/vROsEiq.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {},
   "source": [
    "## Step-by-step tutorial\n",
    "\n",
    "### Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7",
   "metadata": {},
   "source": [
    "### Create an interactive map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "Map = geemap.Map()\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "### Add data to the map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "point = ee.Geometry.Point([-122.4439, 37.7538])\n",
    "# point = ee.Geometry.Point([-87.7719, 41.8799])\n",
    "\n",
    "image = (\n",
    "    ee.ImageCollection(\"LANDSAT/LC08/C01/T1_SR\")\n",
    "    .filterBounds(point)\n",
    "    .filterDate(\"2016-01-01\", \"2016-12-31\")\n",
    "    .sort(\"CLOUD_COVER\")\n",
    "    .first()\n",
    "    .select(\"B[1-7]\")\n",
    ")\n",
    "\n",
    "vis_params = {\"min\": 0, \"max\": 3000, \"bands\": [\"B5\", \"B4\", \"B3\"]}\n",
    "\n",
    "Map.centerObject(point, 8)\n",
    "Map.addLayer(image, vis_params, \"Landsat-8\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11",
   "metadata": {},
   "source": [
    "### Check image properties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "ee.Date(image.get(\"system:time_start\")).format(\"YYYY-MM-dd\").getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "image.get(\"CLOUD_COVER\").getInfo()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14",
   "metadata": {},
   "source": [
    "### Make training dataset\n",
    "\n",
    "There are several ways you can create a region for generating the training dataset.\n",
    "\n",
    "- Draw a shape (e.g., rectangle) on the map and the use `region = Map.user_roi`\n",
    "- Define a geometry, such as `region = ee.Geometry.Rectangle([-122.6003, 37.4831, -121.8036, 37.8288])`\n",
    "- Create a buffer zone around a point, such as `region = ee.Geometry.Point([-122.4439, 37.7538]).buffer(10000)`\n",
    "- If you don't define a region, it will use the image footprint by default"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15",
   "metadata": {},
   "outputs": [],
   "source": [
    "# region = Map.user_roi\n",
    "# region = ee.Geometry.Rectangle([-122.6003, 37.4831, -121.8036, 37.8288])\n",
    "# region = ee.Geometry.Point([-122.4439, 37.7538]).buffer(10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16",
   "metadata": {},
   "source": [
    "In this example, we are going to use the [USGS National Land Cover Database (NLCD)](https://developers.google.com/earth-engine/datasets/catalog/USGS_NLCD) to create label dataset for training\n",
    "\n",
    "\n",
    "![](https://i.imgur.com/7QoRXxu.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "nlcd = ee.Image(\"USGS/NLCD/NLCD2016\").select(\"landcover\").clip(image.geometry())\n",
    "Map.addLayer(nlcd, {}, \"NLCD\")\n",
    "Map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make the training dataset.\n",
    "points = nlcd.sample(\n",
    "    **{\n",
    "        \"region\": image.geometry(),\n",
    "        \"scale\": 30,\n",
    "        \"numPixels\": 5000,\n",
    "        \"seed\": 0,\n",
    "        \"geometries\": True,  # Set this to False to ignore geometries\n",
    "    }\n",
    ")\n",
    "\n",
    "Map.addLayer(points, {}, \"training\", False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(points.size().getInfo())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(points.first().getInfo())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21",
   "metadata": {},
   "source": [
    "### Train the classifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Use these bands for prediction.\n",
    "bands = [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B6\", \"B7\"]\n",
    "\n",
    "\n",
    "# This property of the table stores the land cover labels.\n",
    "label = \"landcover\"\n",
    "\n",
    "# Overlay the points on the imagery to get training.\n",
    "training = image.select(bands).sampleRegions(\n",
    "    **{\"collection\": points, \"properties\": [label], \"scale\": 30}\n",
    ")\n",
    "\n",
    "# Train a CART classifier with default parameters.\n",
    "trained = ee.Classifier.smileCart().train(training, label, bands)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(training.first().getInfo())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24",
   "metadata": {},
   "source": [
    "### Classify the image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Classify the image with the same bands used for training.\n",
    "result = image.select(bands).classify(trained)\n",
    "\n",
    "# # Display the clusters with random colors.\n",
    "Map.addLayer(result.randomVisualizer(), {}, \"classified\")\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26",
   "metadata": {},
   "source": [
    "### Render categorical map\n",
    "\n",
    "To render a categorical map, we can set two image properties: `landcover_class_values` and `landcover_class_palette`. We can use the same style as the NLCD so that it is easy to compare the two maps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27",
   "metadata": {},
   "outputs": [],
   "source": [
    "class_values = nlcd.get(\"landcover_class_values\").getInfo()\n",
    "class_values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "class_palette = nlcd.get(\"landcover_class_palette\").getInfo()\n",
    "class_palette"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "29",
   "metadata": {},
   "outputs": [],
   "source": [
    "landcover = result.set(\"classification_class_values\", class_values)\n",
    "landcover = landcover.set(\"classification_class_palette\", class_palette)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30",
   "metadata": {},
   "outputs": [],
   "source": [
    "Map.addLayer(landcover, {}, \"Land cover\")\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31",
   "metadata": {},
   "source": [
    "### Visualize the result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Change layer opacity:\")\n",
    "cluster_layer = Map.layers[-1]\n",
    "cluster_layer.interact(opacity=(0, 1, 0.1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33",
   "metadata": {},
   "source": [
    "### Add a legend to the map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34",
   "metadata": {},
   "outputs": [],
   "source": [
    "Map.add_legend(builtin_legend=\"NLCD\")\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35",
   "metadata": {},
   "source": [
    "### Export the result\n",
    "\n",
    "Export the result directly to your computer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "out_dir = os.path.join(os.path.expanduser(\"~\"), \"Downloads\")\n",
    "out_file = os.path.join(out_dir, \"landcover.tif\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "37",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_export_image(landcover, filename=out_file, scale=900)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38",
   "metadata": {},
   "source": [
    "Export the result to Google Drive:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_export_image_to_drive(\n",
    "    landcover, description=\"landcover\", folder=\"export\", scale=900\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
